/**
 * @file	
 * @author	chipsea
 * @brief	
 * @version	0.1
 * @date	2020-11-30
 * @copyright Copyright (c) 2020, CHIPSEA Co., Ltd.
 * @note
 */



/*********************************************************************
 * INCLUDES
 */

#include "OSAL.h"
#include "gatt.h"
#include "hci.h"
#include "gapgattserver.h"
#include "gattservapp.h"
#include "gatt_uuid.h"
#include "gatt_profile_uuid.h"
#include "linkdb.h"
#include "peripheral.h"
#include "gapbondmgr.h"
#include "devinfoservice.h"
#include "battservice.h"
#include "scanparamservice.h"


#include "hiddev.h"
#include "hidkbd.h"

#include "hidkbdservice.h"
//#include "touch_key.h"
#include "log.h"

/*********************************************************************
 * MACROS
 */

// Battery measurement period in ms
#define DEFAULT_BATT_PERIOD                   15000

// TRUE to run scan parameters refresh notify test
#define DEFAULT_SCAN_PARAM_NOTIFY_TEST        TRUE

// Advertising intervals (units of 625us, 160=100ms)
#define HID_INITIAL_ADV_INT_MIN               48
#define HID_INITIAL_ADV_INT_MAX               80
#define HID_HIGH_ADV_INT_MIN                  32
#define HID_HIGH_ADV_INT_MAX                  48
#define HID_LOW_ADV_INT_MIN                   1600
#define HID_LOW_ADV_INT_MAX                   1600

// Advertising timeouts in sec
#define HID_INITIAL_ADV_TIMEOUT               60
#define HID_HIGH_ADV_TIMEOUT                  5
#define HID_LOW_ADV_TIMEOUT                   0

// Heart Rate Task Events
#define START_DEVICE_EVT                      0x0001
#define BATT_PERIODIC_EVT                     0x0002
#define HID_IDLE_EVT                          0x0004
#define HID_SEND_REPORT_EVT                   0x0008



#define reportQEmpty()                        ( firstQIdx == lastQIdx )

/*********************************************************************
 * CONSTANTS
 */

#define HID_DEV_DATA_LEN                      8

#ifdef HID_DEV_RPT_QUEUE_LEN
  #define HID_DEV_REPORT_Q_SIZE               (HID_DEV_RPT_QUEUE_LEN+1)
#else
  #define HID_DEV_REPORT_Q_SIZE               (10+1)
#endif

/*********************************************************************
 * TYPEDEFS
 */

typedef struct
{
 uint8 id;
 uint8 type;
 uint8 len;
 uint8 data[HID_DEV_DATA_LEN];
} hidDevReport_t;

/*********************************************************************
 * GLOBAL VARIABLES
 */

// Task ID
uint8 hidDevTaskId;

/*********************************************************************
 * EXTERNAL VARIABLES
 */

/*********************************************************************
 * EXTERNAL FUNCTIONS
 */

// HID report mapping table
 extern hidRptMap_t  hidRptMap[];


/*********************************************************************
 * LOCAL VARIABLES
 */

// GAP State
gaprole_States_t hidDevGapState = GAPROLE_INIT;

// TRUE if connection is secure
static uint8 hidDevConnSecure = FALSE;

// GAP connection handle
 uint16 gapConnHandle;

// TRUE if pairing in progress
static uint8 hidDevPairingStarted = FALSE;

// Status of last pairing
static uint8 pairingStatus = SUCCESS;

static hidRptMap_t *pHidDevRptTbl;

static uint8 hidDevRptTblLen;

static hidDevCB_t *pHidDevCB;

static hidDevCfg_t *pHidDevCfg;

// Whether to change to the preferred connection parameters
static uint8 updateConnParams = TRUE;

// Pending reports
static uint8 firstQIdx = 0;
static uint8 lastQIdx = 0;
static hidDevReport_t hidDevReportQ[HID_DEV_REPORT_Q_SIZE];

// Last report sent out
static attHandleValueNoti_t lastNoti = { 0 };

/*********************************************************************
 * LOCAL FUNCTIONS
 */

static void hidDev_ProcessOSALMsg( osal_event_hdr_t *pMsg );
static void hidDevProcessGattMsg( gattMsgEvent_t *pMsg );
static void hidDevDisconnected( void );
static void hidDevGapStateCB( gaprole_States_t newState );
static void hidDevPairStateCB( uint16 connHandle, uint8 state, uint8 status );
static void hidDevPasscodeCB( uint8 *deviceAddr, uint16 connectionHandle,
                              uint8 uiInputs, uint8 uiOutputs );
void hidDevBattCB( uint8 event );
void hidDevScanParamCB( uint8 event );
static void hidDevBattPeriodicTask( void );
static hidRptMap_t *hidDevRptByHandle( uint16 handle );
static hidRptMap_t *hidDevRptById( uint8 id, uint8 type );
static hidRptMap_t *hidDevRptByCccdHandle( uint16 handle );
static void hidDevEnqueueReport( uint8 id, uint8 type, uint8 len, uint8 *pData );
static hidDevReport_t *hidDevDequeueReport( void );
static void hidDevSendReport( uint8 id, uint8 type, uint8 len, uint8 *pData );
static void hidDevHighAdvertising( void );
static void hidDevLowAdvertising( void );
static void hidDevInitialAdvertising( void );
static uint8 hidDevBondCount( void );
static void hidDevStartIdleTimer( void );
static void hidDevStopIdleTimer( void );
static void HidDev_scanParamCB(uint8_t event);

/*********************************************************************
 * PROFILE CALLBACKS
 */
// GAP Role Callbacks
static gapRolesCBs_t hidDev_PeripheralCBs =
{
  hidDevGapStateCB,   // Profile State Change Callbacks
  NULL                // When a valid RSSI is read from controller
};

// Bond Manager Callbacks
static const gapBondCBs_t hidDevBondCB =
{
  hidDevPasscodeCB,
  hidDevPairStateCB
};




/*********************************************************************
 * PUBLIC FUNCTIONS
 */

/*********************************************************************
 * @fn      HidDev_Init
 *
 * @brief   Initialization function for the Hid Dev Task.
 *          This is called during initialization and should contain
 *          any application specific initialization (ie. hardware
 *          initialization/setup, table initialization, power up
 *          notificaiton ... ).
 *
 * @param   task_id - the ID assigned by OSAL.  This ID should be
 *                    used to send messages and set timers.
 *
 * @return  none
 */
void HidDev_Init( uint8 task_id )
{
  hidDevTaskId = task_id;

  // Setup the GAP Bond Manager
  {
    uint8 syncWL = FALSE;//TRUE;

    // If a bond is created, the HID Device should write the address of the
    // HID Host in the HID Device controller's white list and set the HID
    // Device controller's advertising filter policy to 'process scan and
    // connection requests only from devices in the White List'.
    VOID GAPBondMgr_SetParameter( GAPBOND_AUTO_SYNC_WL, sizeof( uint8 ), &syncWL );
  }

  // Set up services
  GGS_AddService( GATT_ALL_SERVICES );         // GAP
  GATTServApp_AddService( GATT_ALL_SERVICES ); // GATT attributes
  DevInfo_AddService( );
  Batt_AddService();
  ScanParam_AddService();
  
  Batt_Register(NULL);
	
	// Register for Scan Parameters service callback.
  ScanParam_Register(HidDev_scanParamCB);
	
  //touch_init(on_key);
 

  // Setup a delayed profile startup
  osal_set_event( hidDevTaskId, START_DEVICE_EVT );
}

/*********************************************************************
 * @fn      HidDev_ProcessEvent
 *
 * @brief   Hid Dev Task event processor.  This function
 *          is called to process all events for the task.  Events
 *          include timers, messages and any other user defined events.
 *
 * @param   task_id  - The OSAL assigned task ID.
 * @param   events - events to process.  This is a bit map and can
 *                   contain more than one event.
 *
 * @return  events not processed
 */
uint16 HidDev_ProcessEvent( uint8 task_id, uint16 events )
{

  VOID task_id; // OSAL required parameter that isn't used in this function
  LOG("%s\n",__FUNCTION__);

  if ( events & SYS_EVENT_MSG )
  {
    uint8 *pMsg;

    if ( (pMsg = osal_msg_receive( hidDevTaskId )) != NULL )
    {
      hidDev_ProcessOSALMsg( (osal_event_hdr_t *)pMsg );

      // Release the OSAL message
      VOID osal_msg_deallocate( pMsg );
    }

    // return unprocessed events
    return (events ^ SYS_EVENT_MSG);
  }

  if ( events & START_DEVICE_EVT )
  {
    // Start the Device
    VOID GAPRole_StartDevice( &hidDev_PeripheralCBs );

    // Register with bond manager after starting device
    GAPBondMgr_Register( (gapBondCBs_t *) &hidDevBondCB );
	GAPBondMgr_SetParameter(GAPBOND_ERASE_ALLBONDS,0,NULL);
	LOG("start Device EVT\n\r");


	

    return ( events ^ START_DEVICE_EVT );
  }

  if ( events & HID_IDLE_EVT )
  {
    if ( hidDevGapState == GAPROLE_CONNECTED )
    {
      // if pairing in progress then restart timer
      if ( hidDevPairingStarted )
      {
        hidDevStartIdleTimer();
		LOG("hidDevStartIdleTimer \n\r");
      }
      // else disconnect
      else
      {
        GAPRole_TerminateConnection();
      }
    }

    return ( events ^ HID_IDLE_EVT );
  }

  if ( events & BATT_PERIODIC_EVT )
  {
    // Perform periodic battery task
    hidDevBattPeriodicTask();

    return ( events ^ BATT_PERIODIC_EVT );
  }

  if ( events & HID_SEND_REPORT_EVT )
  {
    // if connection is secure
    if ( hidDevConnSecure )
    {
			LOG("Send Hid Report\n\r");
      hidDevReport_t *pReport = hidDevDequeueReport();

      if ( pReport != NULL )
      {
        // Send report
        hidDevSendReport( pReport->id, pReport->type, pReport->len, pReport->data );
      }

      return ( reportQEmpty() ? events ^ HID_SEND_REPORT_EVT : events );
    }

    return ( events ^ HID_SEND_REPORT_EVT );
  }

  return 0;
}

/*********************************************************************
 * @fn      HidDev_Register
 *
 * @brief   Register a callback function with HID Dev.
 *
 * @param   pCfg - Parameter configuration.
 * @param   pfnServiceCB - Callback function.
 *
 * @return  None.
 */
void HidDev_Register( hidDevCfg_t *pCfg, hidDevCB_t *pCBs )
{
  pHidDevCB = pCBs;
  pHidDevCfg = pCfg;
}

/*********************************************************************
 * @fn      HidDev_RegisterReports
 *
 * @brief   Register the report table with HID Dev.
 *
 * @param   numReports - Length of report table.
 * @param   pRpt - Report table.
 *
 * @return  None.
 */
void HidDev_RegisterReports( uint8 numReports, hidRptMap_t *pRpt )
{
  pHidDevRptTbl = pRpt;
  hidDevRptTblLen = numReports;
}

/*********************************************************************
 * @fn      HidDev_Report
 *
 * @brief   Send a HID report.
 *
 * @param   id - HID report ID.
 * @param   type - HID report type.
 * @param   len - Length of report.
 * @param   pData - Report data.
 *
 * @return  None.
 */
void HidDev_Report( uint8 id, uint8 type, uint8 len, uint8*pData )
{
  // if connected
  if ( hidDevGapState == GAPROLE_CONNECTED )
  {
    // if connection is secure
    if ( hidDevConnSecure )
    {
      // Make sure there're no pending reports
      if ( reportQEmpty() )
      {
        // send report
        hidDevSendReport( id, type, len, pData );
		
        LOG("send key action\n\r");
        return; // we're done
      }
    }
  }
  // else if not already advertising
  else if ( hidDevGapState != GAPROLE_ADVERTISING )
  {
    // if bonded
    if ( hidDevBondCount() > 0 )
    {
      // start high duty cycle advertising
      hidDevHighAdvertising();
    }
    // else not bonded
    else
    {
      // start initial advertising
      hidDevInitialAdvertising();
    }
  }

  // hidDev task will send report when secure connection is established
  hidDevEnqueueReport( id, type, len, pData );
}

/*********************************************************************
 * @fn      HidDev_Close
 *
 * @brief   Close the connection or stop advertising.
 *
 * @return  None.
 */
void HidDev_Close( void )
{
  uint8 param;

  // if connected then disconnect
  if ( hidDevGapState == GAPROLE_CONNECTED )
  {
    GAPRole_TerminateConnection();
  }
  // else stop advertising
  else
  {
    param = FALSE;
    GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &param );
  }
}

/*********************************************************************
 * @fn      HidDev_SetParameter
 *
 * @brief   Set a HID Dev parameter.
 *
 * @param   param - Profile parameter ID
 * @param   len - length of data to right
 * @param   pValue - pointer to data to write.  This is dependent on
 *          the parameter ID and WILL be cast to the appropriate
 *          data type (example: data type of uint16 will be cast to
 *          uint16 pointer).
 *
 * @return  bStatus_t
 */
bStatus_t HidDev_SetParameter( uint8 param, uint8 len, void *pValue )
{
  bStatus_t ret = SUCCESS;

  switch ( param )
  {
    case HIDDEV_ERASE_ALLBONDS:
      if ( len == 0 )
      {
        // See if the last report sent out wasn't a release key
        if ( osal_isbufset( lastNoti.value, 0x00, lastNoti.len ) == FALSE )
        {
          // Send a release report before disconnecting, otherwise
          // the last pressed key would get 'stuck' on the HID Host.
          osal_memset( lastNoti.value, 0x00, lastNoti.len );

          GATT_Notification( gapConnHandle, &lastNoti, FALSE );
        }

        // Drop connection
        if ( hidDevGapState == GAPROLE_CONNECTED )
        {
          GAPRole_TerminateConnection();
        }

        // Flush report queue
        firstQIdx = lastQIdx = 0;

        // Erase bonding info
        GAPBondMgr_SetParameter( GAPBOND_ERASE_ALLBONDS, 0, NULL );
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;

    default:
      ret = INVALIDPARAMETER;
      break;
  }

  return ( ret );
}

/*********************************************************************
 * @fn      HidDev_GetParameter
 *
 * @brief   Get a HID Dev parameter.
 *
 * @param   param - Profile parameter ID
 * @param   pValue - pointer to data to get.  This is dependent on
 *          the parameter ID and WILL be cast to the appropriate
 *          data type (example: data type of uint16 will be cast to
 *          uint16 pointer).
 *
 * @return  bStatus_t
 */
bStatus_t HidDev_GetParameter( uint8 param, void *pValue )
{
  bStatus_t ret = SUCCESS;

  switch ( param )
  {

    default:
      ret = INVALIDPARAMETER;
      break;
  }

  return ( ret );
}

/*********************************************************************
 * @fn      HidDev_PasscodeRsp
 *
 * @brief   Respond to a passcode request.
 *
 * @param   status - SUCCESS if passcode is available, otherwise
 *                   see @ref SMP_PAIRING_FAILED_DEFINES.
 * @param   passcode - integer value containing the passcode.
 *
 * @return  none
 */
void HidDev_PasscodeRsp( uint8 status, uint32 passcode )
{
  // Send passcode response
  GAPBondMgr_PasscodeRsp( gapConnHandle, status, passcode );
}

/*********************************************************************
 * @fn          HidDev_ReadAttrCB
 *
 * @brief       HID Dev attribute read callback.
 *
 * @param       connHandle - connection message was received on
 * @param       pAttr - pointer to attribute
 * @param       pValue - pointer to data to be read
 * @param       pLen - length of data to be read
 * @param       offset - offset of the first octet to be read
 * @param       maxLen - maximum length of data to be read
 * @param       method - type of read message 
 *
 * @return      SUCCESS, blePending or Failure
 */
uint8 HidDev_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                         uint8 *pValue, uint16 *pLen, uint16 offset, uint8 maxLen )
{
  bStatus_t   status = SUCCESS;
  hidRptMap_t *pRpt;

  uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);

  // Only report map is long
  if ( offset > 0 && uuid != REPORT_MAP_UUID )
  {
    return ( ATT_ERR_ATTR_NOT_LONG );
  }

  if ( uuid == REPORT_UUID ||
       uuid == BOOT_KEY_INPUT_UUID ||
       uuid == BOOT_KEY_OUTPUT_UUID ||
       uuid == BOOT_MOUSE_INPUT_UUID )
  {
    // find report ID in table
    if ( (pRpt = hidDevRptByHandle(pAttr->handle)) != NULL )
    {
      // execute report callback
      status  = (*pHidDevCB->reportCB)( pRpt->id, pRpt->type, uuid,
                                        HID_DEV_OPER_READ, pLen, pValue );
    }
    else
    {
      *pLen = 0;
    }
  }
  else if ( uuid == REPORT_MAP_UUID )
  {
    // verify offset
    if ( offset >= hidReportMapLen )
    {
      status = ATT_ERR_INVALID_OFFSET;
    }
    else
    {
      // determine read length
      *pLen = MIN( maxLen, (hidReportMapLen - offset) );

      // copy data
      osal_memcpy( pValue, pAttr->pValue + offset, *pLen );
    }
  }
  else if ( uuid == HID_INFORMATION_UUID )
  {
    *pLen = HID_INFORMATION_LEN;
    osal_memcpy( pValue, pAttr->pValue, HID_INFORMATION_LEN );
  }
  else if ( uuid == GATT_REPORT_REF_UUID )
  {
    *pLen = HID_REPORT_REF_LEN;
    osal_memcpy( pValue, pAttr->pValue, HID_REPORT_REF_LEN );
  }
  else if ( uuid == PROTOCOL_MODE_UUID )
  {
    *pLen = HID_PROTOCOL_MODE_LEN;
    pValue[0] = pAttr->pValue[0];
  }
  else if ( uuid == GATT_EXT_REPORT_REF_UUID )
  {
    *pLen = HID_EXT_REPORT_REF_LEN;
    osal_memcpy( pValue, pAttr->pValue, HID_EXT_REPORT_REF_LEN );
  }

  // restart idle timer
  if ( status == SUCCESS )
  {
    hidDevStartIdleTimer();
  }

  return ( status );
}

/*********************************************************************
 * @fn      HidDev_WriteAttrCB
 *
 * @brief   HID Dev attribute read callback.
 *
 * @param   connHandle - connection message was received on
 * @param   pAttr - pointer to attribute
 * @param   pValue - pointer to data to be written
 * @param   len - length of data
 * @param   offset - offset of the first octet to be written
 *
 * @return  Success or Failure
 */
bStatus_t HidDev_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                              uint8 *pValue, uint16 len, uint16 offset )
{
  bStatus_t status = SUCCESS;
  hidRptMap_t *pRpt;

  // Make sure it's not a blob operation (no attributes in the profile are long)
  if ( offset > 0 )
  {
    return ( ATT_ERR_ATTR_NOT_LONG );
  }

  uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);

  if ( uuid == REPORT_UUID ||
       uuid == BOOT_KEY_OUTPUT_UUID )
  {
    // find report ID in table
    if ((pRpt = hidDevRptByHandle(pAttr->handle)) != NULL)
    {
      // execute report callback
      status  = (*pHidDevCB->reportCB)( pRpt->id, pRpt->type, uuid,
                                        HID_DEV_OPER_WRITE, &len, pValue );
    }
  }
  else if ( uuid == HID_CTRL_PT_UUID )
  {
    // Validate length and value range
    if ( len == 1 )
    {
      if ( pValue[0] == HID_CMD_SUSPEND ||  pValue[0] == HID_CMD_EXIT_SUSPEND )
      {
        // execute HID app event callback
        (*pHidDevCB->evtCB)( (pValue[0] == HID_CMD_SUSPEND) ?
                             HID_DEV_SUSPEND_EVT : HID_DEV_EXIT_SUSPEND_EVT );
      }
      else
      {
        status = ATT_ERR_INVALID_VALUE;
      }
    }
    else
    {
      status = ATT_ERR_INVALID_VALUE_SIZE;
    }
  }
  else if ( uuid == GATT_CLIENT_CHAR_CFG_UUID )
  {
    status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,
                                             offset, GATT_CLIENT_CFG_NOTIFY );
    if ( status == SUCCESS )
    {
      uint16 charCfg = BUILD_UINT16( pValue[0], pValue[1] );

      // find report ID in table
      if ( (pRpt = hidDevRptByCccdHandle(pAttr->handle)) != NULL )
      {
	
	    if(hidRptMap[5].cccdHandle==pRpt->cccdHandle)
	    {
         LOG("Audio cfg%4X\n\r",charCfg);
		  }
        // execute report callback
        (*pHidDevCB->reportCB)( pRpt->id, pRpt->type, uuid,
                                (charCfg == GATT_CLIENT_CFG_NOTIFY) ?
                                  HID_DEV_OPER_ENABLE : HID_DEV_OPER_DISABLE,
                                &len, pValue );
      }
    }
  }
  else if ( uuid == PROTOCOL_MODE_UUID )
  {
    if ( len == HID_PROTOCOL_MODE_LEN )
    {
      if ( pValue[0] == HID_PROTOCOL_MODE_BOOT ||
           pValue[0] == HID_PROTOCOL_MODE_REPORT )
      {
        pAttr->pValue[0] = pValue[0];

        // execute HID app event callback
        (*pHidDevCB->evtCB)( (pValue[0] == HID_PROTOCOL_MODE_BOOT) ?
                             HID_DEV_SET_BOOT_EVT : HID_DEV_SET_REPORT_EVT );
      }
      else
      {
        status = ATT_ERR_INVALID_VALUE;
      }
    }
    else
    {
      status = ATT_ERR_INVALID_VALUE_SIZE;
    }
  }

  // restart idle timer
  if (status == SUCCESS)
  {
    hidDevStartIdleTimer();
  }

  return ( status );
}

/*********************************************************************
 * @fn      hidDev_ProcessOSALMsg
 *
 * @brief   Process an incoming task message.
 *
 * @param   pMsg - message to process
 *
 * @return  none
 */
static void hidDev_ProcessOSALMsg( osal_event_hdr_t *pMsg )
{
  switch ( pMsg->event )
  {
  case GATT_MSG_EVENT:
      hidDevProcessGattMsg( (gattMsgEvent_t *) pMsg );
      break;

  default:
      break;
  }
}

/*********************************************************************
 * @fn      hidDevProcessGattMsg
 *
 * @brief   Process GATT messages
 *
 * @return  none
 */
static void hidDevProcessGattMsg( gattMsgEvent_t *pMsg )
{

}

/*********************************************************************
 * @fn          hidDevHandleConnStatusCB
 *
 * @brief       Reset client char config.
 *
 * @param       connHandle - connection handle
 * @param       changeType - type of change
 *
 * @return      none
 */
static void hidDevHandleConnStatusCB( uint16 connHandle, uint8 changeType )
{
  uint8           i;
  hidRptMap_t     *p = pHidDevRptTbl;
  uint16          retHandle;
  gattAttribute_t *pAttr;

  // Make sure this is not loopback connection
  if ( connHandle != LOOPBACK_CONNHANDLE )
  {
    if ( ( changeType == LINKDB_STATUS_UPDATE_REMOVED )      ||
         ( ( changeType == LINKDB_STATUS_UPDATE_STATEFLAGS ) &&
           ( !linkDB_Up( connHandle ) ) ) )
    {
      for ( i = hidDevRptTblLen; i > 0; i--, p++ )
      {
        if ( p->cccdHandle != 0 )
        {
          if ( (pAttr = GATT_FindHandle(p->cccdHandle, &retHandle)) != NULL )
          {
            GATTServApp_InitCharCfg( connHandle, (gattCharCfg_t *) pAttr->pValue );
          }
        }
      }
    }
  }
}

/*********************************************************************
 * @fn      hidDevDisconnected
 *
 * @brief   Handle disconnect.
 *
 * @return  none
 */
static void hidDevDisconnected( void )
{
  // Stop idle timer
  hidDevStopIdleTimer();

  // Reset client characteristic configuration descriptors
  Batt_HandleConnStatusCB( gapConnHandle, LINKDB_STATUS_UPDATE_REMOVED );
  //ScanParam_HandleConnStatusCB( gapConnHandle, LINKDB_STATUS_UPDATE_REMOVED );
  hidDevHandleConnStatusCB( gapConnHandle, LINKDB_STATUS_UPDATE_REMOVED );

  // Reset state variables
  hidDevConnSecure = FALSE;
  hidProtocolMode = HID_PROTOCOL_MODE_REPORT;
  hidDevPairingStarted = FALSE;

  // Reset last report sent out
  osal_memset( &lastNoti, 0, sizeof( attHandleValueNoti_t ) );

  // if bonded and normally connectable start advertising
  if ( ( hidDevBondCount() > 0 ) &&
       ( pHidDevCfg->hidFlags & HID_FLAGS_NORMALLY_CONNECTABLE ) )
  {
    hidDevLowAdvertising();
  }
}

/*********************************************************************
 * @fn      hidDevGapStateCB
 *
 * @brief   Notification from the profile of a state change.
 *
 * @param   newState - new state
 *
 * @return  none
 */
void hidDevGapStateCB( gaprole_States_t newState )
{
  LOG("%s, %d\n",__FUNCTION__, newState);
  // if connected
  if ( newState == GAPROLE_CONNECTED )
  {
    // get connection handle
    GAPRole_GetParameter( GAPROLE_CONNHANDLE, &gapConnHandle );

    // connection not secure yet
    hidDevConnSecure = FALSE;

    // don't start advertising when connection is closed
    uint8 param = FALSE;
    GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &param );

	uint8 peerAddress[B_ADDR_LEN];

    GAPRole_GetParameter(GAPROLE_CONN_BD_ADDR, peerAddress);

	LOG("Master Mac:%02X,%02X,%02X,%02X,%02X,%02X\n\r",peerAddress[5],peerAddress[4],peerAddress[3],peerAddress[2],peerAddress[1],peerAddress[0]);
	
	

    // start idle timer
    hidDevStartIdleTimer();
  }
  // if disconnected
  else if ( hidDevGapState == GAPROLE_CONNECTED &&
            newState != GAPROLE_CONNECTED )
  {
 //  GAPBondMgr_SetParameter(GAPBOND_ERASE_ALLBONDS,0,NULL);
    hidDevDisconnected();
    updateConnParams = TRUE;

    if ( pairingStatus == SMP_PAIRING_FAILED_CONFIRM_VALUE )
    {
      // bonding failed due to mismatched confirm values
      hidDevInitialAdvertising();

      pairingStatus = SUCCESS;
    }
  }
  // if started
  else if ( newState == GAPROLE_STARTED )
  {
    // nothing to do for now!
  }

  hidDevGapState = newState;
}

/*********************************************************************
 * @fn      hidDevPairStateCB
 *
 * @brief   Pairing state callback.
 *
 * @return  none
 */
static void hidDevPairStateCB( uint16 connHandle, uint8 state, uint8 status )
{
  if ( state == GAPBOND_PAIRING_STATE_STARTED )
  {
    hidDevPairingStarted = TRUE;
  }
  else if ( state == GAPBOND_PAIRING_STATE_COMPLETE )
  {
    hidDevPairingStarted = FALSE;

    if ( status == SUCCESS )
    {
      hidDevConnSecure = TRUE;
			LOG("Pair Success\n\r");
	 	
    }
		else
		{
		  LOG("Pair Fail\n\r");
		}

    pairingStatus = status;
  }
  else if ( state == GAPBOND_PAIRING_STATE_BONDED )
  {
    if ( status == SUCCESS )
    {
      hidDevConnSecure = TRUE;
    }
  }
  if ( !reportQEmpty() && hidDevConnSecure )
  {
		 LOG("Set Send Report EVENT\n\r");
    // Notify our task to send out pending reports
    osal_set_event( hidDevTaskId, HID_SEND_REPORT_EVT );
  }
}

/*********************************************************************
 * @fn      hidDevPasscodeCB
 *
 * @brief   Passcode callback.
 *
 * @param   deviceAddr - address of device to pair with, and could be either public or random.
 * @param   connectionHandle - connection handle
 * @param   uiInputs - pairing User Interface Inputs - Ask user to input passcode
 * @param   uiOutputs - pairing User Interface Outputs - Display passcode
 *
 * @return  none
 */
void hidDevPasscodeCB( uint8 *deviceAddr, uint16 connectionHandle,
                                        uint8 uiInputs, uint8 uiOutputs )
{
  if ( pHidDevCB && pHidDevCB->passcodeCB )
  {
    // execute HID app passcode callback
    (*pHidDevCB->passcodeCB)( deviceAddr, connectionHandle, uiInputs, uiOutputs );
  }
  else
  {
    // Send passcode response
    GAPBondMgr_PasscodeRsp( connectionHandle, SUCCESS, 0 );
  }
}

/*********************************************************************
 * @fn      hidDevBattCB
 *
 * @brief   Callback function for battery service.
 *
 * @param   event - service event
 *
 * @return  none
 */
void hidDevBattCB( uint8 event )
{
  if ( event == BATT_LEVEL_NOTI_ENABLED )
  {
    // if connected start periodic measurement
    if ( hidDevGapState == GAPROLE_CONNECTED )
    {
      osal_start_timerEx( hidDevTaskId, BATT_PERIODIC_EVT, DEFAULT_BATT_PERIOD );
    }
  }
  else if ( event == BATT_LEVEL_NOTI_DISABLED )
  {
    // stop periodic measurement
    osal_stop_timerEx( hidDevTaskId, BATT_PERIODIC_EVT );
  }
}

/*********************************************************************
 * @fn      hidDevScanParamCB
 *
 * @brief   Callback function for scan parameter service.
 *
 * @param   event - service event
 *
 * @return  none
 */
 void hidDevScanParamCB( uint8 event )
{

}

/*********************************************************************
 * @fn      hidDevBattPeriodicTask
 *
 * @brief   Perform a periodic task for battery measurement.
 *
 * @param   none
 *
 * @return  none
 */
static void hidDevBattPeriodicTask( void )
{
  if ( hidDevGapState == GAPROLE_CONNECTED )
  {
    // perform battery level check
    Batt_MeasLevel( );

    // Restart timer
    osal_start_timerEx( hidDevTaskId, BATT_PERIODIC_EVT, DEFAULT_BATT_PERIOD );
  }
}

/*********************************************************************
 * @fn      hidDevRptByHandle
 *
 * @brief   Find the HID report structure for the given handle.
 *
 * @param   handle - ATT handle
 *
 * @return  Pointer to HID report structure
 */
static hidRptMap_t *hidDevRptByHandle( uint16 handle )
{
  uint8       i;
  hidRptMap_t *p = pHidDevRptTbl;

  for ( i = hidDevRptTblLen; i > 0; i--, p++ )
  {
    if ( p->handle == handle && p->mode == hidProtocolMode)
    {
      return p;
    }
  }

  return NULL;
}

/*********************************************************************
 * @fn      hidDevRptByCccdHandle
 *
 * @brief   Find the HID report structure for the given CCC handle.
 *
 * @param   handle - ATT handle
 *
 * @return  Pointer to HID report structure
 */
static hidRptMap_t *hidDevRptByCccdHandle( uint16 handle )
{
  uint8       i;
  hidRptMap_t *p = pHidDevRptTbl;

  for ( i = hidDevRptTblLen; i > 0; i--, p++ )
  {
    if ( p->cccdHandle == handle)
    {
      if(i==HID_VOICE_START_IN_CCCD_IDX)
      {
       LOG("Voice Notify Enable!!!!!\n\r");
	  }
      return p;
    }
  }

  return NULL;
}

/*********************************************************************
 * @fn      hidDevRptById
 *
 * @brief   Find the HID report structure for the Report ID and type.
 *
 * @param   id - HID report ID
 * @param   type - HID report type
 *
 * @return  Pointer to HID report structure
 */
static hidRptMap_t *hidDevRptById( uint8 id, uint8 type )
{
  uint8       i;
  hidRptMap_t *p = pHidDevRptTbl;

  for ( i = hidDevRptTblLen; i > 0; i--, p++ )
  {
    if ( p->id == id && p->type == type && p->mode == hidProtocolMode )
    {
      return p;
    }
  }

  return NULL;
}

/*********************************************************************
 * @fn      hidDevSendReport
 *
 * @brief   Send a HID report.
 *
 * @param   id - HID report ID.
 * @param   type - HID report type.
 * @param   len - Length of report.
 * @param   pData - Report data.
 *
 * @return  None.
 */
static void hidDevSendReport( uint8 id, uint8 type, uint8 len, uint8 *pData )
{
  hidRptMap_t           *pRpt;
  gattAttribute_t       *pAttr;
  uint16                retHandle;

  LOG("%s\n",__FUNCTION__);
  // Get ATT handle for report
  if ( (pRpt = hidDevRptById(id, type)) != NULL )
  {
    // if notifications are enabled
    if ( (pAttr = GATT_FindHandle(pRpt->cccdHandle, &retHandle)) != NULL )
    {
      uint16 value;

      value  = GATTServApp_ReadCharCfg( gapConnHandle, (gattCharCfg_t *) pAttr->pValue );
      if ( value & GATT_CLIENT_CFG_NOTIFY )
      {
        // After service discovery and encryption, the HID Device should request to
        // change to the preferred connection parameters that best suit its use case.
        if ( updateConnParams )
        {
          GAPRole_SetParameter( GAPROLE_PARAM_UPDATE_REQ, sizeof( uint8 ), &updateConnParams );
          updateConnParams = FALSE;
        }

        // send notification
        lastNoti.handle = pRpt->handle;
        lastNoti.len = len;
        osal_memcpy(lastNoti.value, pData, len);

        GATT_Notification( gapConnHandle, &lastNoti, FALSE );

        // start idle timer
        hidDevStartIdleTimer();
      }
	  else
	  {
       LOG("notify fail\n\r");
	  }
    }
  }
}

/*********************************************************************
 * @fn      hidDevEnqueueReport
 *
 * @brief   Enqueue a HID report to be sent later.
 *
 * @param   id - HID report ID.
 * @param   type - HID report type.
 * @param   len - Length of report.
 * @param   pData - Report data.
 *
 * @return  None.
 */
static void hidDevEnqueueReport( uint8 id, uint8 type, uint8 len, uint8 *pData )
{
  // Enqueue only if bonded
  if ( hidDevBondCount() > 0 )
  {
    // Update last index
    lastQIdx = ( lastQIdx + 1 ) % HID_DEV_REPORT_Q_SIZE;

    if ( lastQIdx == firstQIdx )
    {
      // Queue overflow; discard oldest report
      firstQIdx = ( firstQIdx + 1 ) % HID_DEV_REPORT_Q_SIZE;
    }

    // Save report
    hidDevReportQ[lastQIdx].id = id;
    hidDevReportQ[lastQIdx].type = type;
    hidDevReportQ[lastQIdx].len = len;
    osal_memcpy( hidDevReportQ[lastQIdx].data, pData, len );

    if ( hidDevConnSecure )
    {
      // Notify our task to send out pending reports
      osal_set_event( hidDevTaskId, HID_SEND_REPORT_EVT );
    }
  }
}

/*********************************************************************
 * @fn      hidDevDequeueReport
 *
 * @brief   Dequeue a HID report to be sent out.
 *
 * @param   id - HID report ID.
 * @param   type - HID report type.
 * @param   len - Length of report.
 * @param   pData - Report data.
 *
 * @return  None.
 */
static hidDevReport_t *hidDevDequeueReport( void )
{
  if ( reportQEmpty() )
  {
    return NULL;
  }

  // Update first index
  firstQIdx = ( firstQIdx + 1 ) % HID_DEV_REPORT_Q_SIZE;

  return ( &(hidDevReportQ[firstQIdx]) );
}

/*********************************************************************
 * @fn      hidDevHighAdvertising
 *
 * @brief   Start advertising at a high duty cycle.

 * @param   None.
 *
 * @return  None.
 */
static void hidDevHighAdvertising( void )
{
  uint8 param;

  VOID GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MIN, HID_HIGH_ADV_INT_MIN );
  VOID GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MAX, HID_HIGH_ADV_INT_MAX );
  VOID GAP_SetParamValue( TGAP_LIM_ADV_TIMEOUT, HID_HIGH_ADV_TIMEOUT );

  // Setup adverstising filter policy first
  param = GAP_FILTER_POLICY_WHITE;
  VOID GAPRole_SetParameter( GAPROLE_ADV_FILTER_POLICY, sizeof( uint8 ), &param );

  param = TRUE;
  GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &param );
}

/*********************************************************************
 * @fn      hidDevLowAdvertising
 *
 * @brief   Start advertising at a low duty cycle.
 *
 * @param   None.
 *
 * @return  None.
 */
static void hidDevLowAdvertising( void )
{
  uint8 param;

  VOID GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MIN, HID_LOW_ADV_INT_MIN );
  VOID GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MAX, HID_LOW_ADV_INT_MAX );
  VOID GAP_SetParamValue( TGAP_LIM_ADV_TIMEOUT, HID_LOW_ADV_TIMEOUT );

  // Setup adverstising filter policy first
  param = GAP_FILTER_POLICY_ALL;//GAP_FILTER_POLICY_WHITE teddy modify
  VOID GAPRole_SetParameter( GAPROLE_ADV_FILTER_POLICY, sizeof( uint8 ), &param );

  param = TRUE;
  VOID GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &param );
}

/*********************************************************************
 * @fn      hidDevInitialAdvertising
 *
 * @brief   Start advertising for initial connection
 *
 * @return  None.
 */
static void hidDevInitialAdvertising( void )
{
  uint8 param;

  VOID GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MIN, HID_INITIAL_ADV_INT_MIN );
  VOID GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MAX, HID_INITIAL_ADV_INT_MAX );
  VOID GAP_SetParamValue( TGAP_LIM_ADV_TIMEOUT, HID_INITIAL_ADV_TIMEOUT );

  // Setup adverstising filter policy first
  param = GAP_FILTER_POLICY_ALL;
  VOID GAPRole_SetParameter( GAPROLE_ADV_FILTER_POLICY, sizeof( uint8 ), &param );

  param = TRUE;
  VOID GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &param );
}

/*********************************************************************
 * @fn      hidDevBondCount
 *
 * @brief   Gets the total number of bonded devices.
 *
 * @param   None.
 *
 * @return  number of bonded devices.
 */
static uint8 hidDevBondCount( void )
{
  uint8 bondCnt = 0;

  VOID GAPBondMgr_GetParameter( GAPBOND_BOND_COUNT, &bondCnt );

  return ( bondCnt );
}

/*********************************************************************
 * @fn      hidDevStartIdleTimer
 *
 * @brief   Start the idle timer.
 *
 * @return  None.
 */
static void hidDevStartIdleTimer( void )
{
  if ( pHidDevCfg->idleTimeout > 0 )
  {
    osal_start_timerEx( hidDevTaskId, HID_IDLE_EVT, pHidDevCfg->idleTimeout );
  }
}

/*********************************************************************
 * @fn      hidDevStopIdleTimer
 *
 * @brief   Stop the idle timer.
 *
 * @return  None.
 */
static void hidDevStopIdleTimer( void )
{
  osal_stop_timerEx( hidDevTaskId, HID_IDLE_EVT );
}


/*********************************************************************
 * @fn      HidDev_scanParamCB
 *
 * @brief   Callback function for scan parameter service.
 *
 * @param   event - service event
 *
 * @return  none
 */
static void HidDev_scanParamCB(uint8_t event)
{
  // Do nothing.
}


/*********************************************************************
*********************************************************************/
